﻿/* Copyright (c) 2010 Lexalytics Inc.

 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation
 files (the "Software"), to deal in the Software without
 restriction, including without limitation the rights to use,
 copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the
 Software is furnished to do so, subject to the following
 conditions:

 The above copyright notice and this permission notice shall be
 included in all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 OTHER DEALINGS IN THE SOFTWARE.
*/

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Xml;

// Needed for Lexalytics business classes
using LexaScopeNET;

namespace Lexalytics4Excel.Dialogs
{
    /// <summary>
    /// Dialog for entering/editing details of a Lexascope profile
    /// </summary>
    public partial class ProfileContents : Form
    {
        // Private member data
        private bool _ValidName = true;
        private bool _ValidURL = true;
        private bool _ValidAccount = true;
        private bool _ValidKey = true;
        private List<string> _CurrentProfiles = new List<string>();

        // Public properties
        /// <summary>Descriptive name for the profile</summary>
        public string ProfileName { get { return ProfileNameText.Text; } }

        /// <summary>Indicator that profile is local versus online</summary>
        public bool LocalProfile { get { return rdoProfileLocal.Checked; } }

        /// <summary>URL to the web service endpoint for the profile</summary>
        public string ServiceURL { get { return ServiceURLText.Text; } }

        /// <summary>Public key portion of key pair associated with profile</summary>
        public string PublicKey { get { return UserKeyText.Text; } }

        /// <summary>Private key portion of key pair associated with profile</summary>
        public string PrivateKey { get { return EncodedKeyText.Text; } }

        /// <summary>Path to license file</summary>
        public string LicensePath { get { return txtLicensePath.Text; } }

        /// <summary>Path to data directory</summary>
        public string DataDirectory { get { return txtDataDirectory.Text; } }

        /// <summary>Path to user directory</summary>
        public string UserDirectory { get { return txtUserDirectory.Text; } }

        /// <summary>
        /// Dialog constructor for use when dialog creates new profile.
        /// </summary>
        /// <param name="CurrentProfiles">A list of current profile names to avoid name conflicts.</param>
        public ProfileContents(List<string> CurrentProfiles)
        {
            InitializeComponent();
            ProfileInstructions.Text = "Enter a descriptive name for the profile and enter the configuration needed for the profile.";
            rdoProfileLocal.Checked = true;
            _CurrentProfiles = CurrentProfiles;
        }

        /// <summary>
        /// Dialog constructor for "edit mode", which provides the details of a profile to edit.
        /// </summary>
        /// <param name="ProfileName">Descriptive name for the profile, cannot be editted.</param>
        /// <param name="ServiceURL">Web service endpoint URL</param>
        /// <param name="PublicKey">Public key for the profile</param>
        /// <param name="PrivateKey">Private key for the profile</param>
        public ProfileContents(string ProfileName, bool LocalProfile, string ServiceURL, string PublicKey, string PrivateKey, string LicensePath, string DataDirectory, string UserDirectory)
        {
            InitializeComponent();
            ProfileInstructions.Text = "The name of the profile may not be modified, however profile connection information may be updated.";
            ProfileNameText.Text = ProfileName;
            ProfileNameText.ReadOnly = true;
            if (LocalProfile)
            {
                rdoProfileLocal.Checked = true;
                txtLicensePath.Text = LicensePath;
                txtDataDirectory.Text = DataDirectory;
                txtUserDirectory.Text = UserDirectory;
            }
            else
            {
                rdoProfileLocal.Checked = false;
                ServiceURLText.Text = ServiceURL;
                UserKeyText.Text = PublicKey;
                EncodedKeyText.Text = PrivateKey;
            }
        }

        #region Button handlers
        private void OKButton_Click(object sender, EventArgs e)
        {
            // Confirm valid input for ProfileName, ServiceURL, User key, and encoded key
            ProfileNameText.Focus();
            if (!Validate())
            {
                DialogResult = DialogResult.None;
                return;
            }

            if (rdoProfileOnline.Checked)
            {
                ServiceURLText.Focus();
                if (!Validate())
                {
                    DialogResult = DialogResult.None;
                    return;
                }
                UserKeyText.Focus();
                if (!Validate())
                {
                    DialogResult = DialogResult.None;
                    return;
                }
                EncodedKeyText.Focus();
                if (!Validate())
                {
                    DialogResult = DialogResult.None;
                    return;
                }
            }
            else
            {
                txtLicensePath.Focus();
                if (!Validate())
                {
                    DialogResult = DialogResult.None;
                    return;
                }
                txtDataDirectory.Focus();
                if (!Validate())
                {
                    DialogResult = DialogResult.None;
                    return;
                }
                txtUserDirectory.Focus();
                if (!Validate())
                {
                    DialogResult = DialogResult.None;
                    return;
                }
            }

            this.DialogResult = DialogResult.OK;
            this.Close();
        }

        private void CancelButton_Click(object sender, EventArgs e)
        {
            this.DialogResult = DialogResult.Cancel;
            this.Close();
        }
        #endregion // Button handlers

        #region Textbox event handlers
        /// <summary>
        /// Invalidate the OK button when Profile Name changes until we confirm it's valid
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ProfileName_TextChanged(object sender, EventArgs e)
        {
            if (ProfileNameText.Text.Length > 0)
                AdvancedOptionsErrors.SetError(ProfileNameText, "");
            _ValidName = false;
        }

        /// <summary>
        /// Invalidate the OK button when URL changes until we confirm it's valid
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ServiceURLText_TextChanged(object sender, EventArgs e)
        {
            if (ServiceURLText.Text.Length > 0)
                AdvancedOptionsErrors.SetError(ServiceURLText, "");
            _ValidURL = false;
        }

        /// <summary>
        /// Invalidate the OK button when Account key changes until we confirm it's valid
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UserKeyText_TextChanged(object sender, EventArgs e)
        {
            if (UserKeyText.Text.Length > 0)
                AdvancedOptionsErrors.SetError(UserKeyText, "");
            _ValidAccount = false;
        }

        /// <summary>
        /// Invalidate the OK button when Account key changes until we confirm it's valid
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void EncodedKeyText_TextChanged(object sender, EventArgs e)
        {
            if (EncodedKeyText.Text.Length > 0)
                AdvancedOptionsErrors.SetError(EncodedKeyText, "");
            _ValidKey = false;
        }

        /// <summary>
        /// Event handler for validation request, calls validation method for profile name
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ProfileName_Validating(object sender, CancelEventArgs e)
        {
            try
            {
                _ValidName = CheckProfileName(ProfileNameText.Text);
                if (_ValidName)
                {
                    AdvancedOptionsErrors.SetError(ProfileNameText, "");
                }
                else
                {
                    AdvancedOptionsErrors.SetError(ProfileNameText, "A valid profile name is required");
                    e.Cancel = true;
                }

                if (_CurrentProfiles.Contains(ProfileNameText.Text))
                {
                    AdvancedOptionsErrors.SetError(ProfileNameText, "A profile with the same name already exists");
                    e.Cancel = true;
                }
            }
            catch (Exception err)
            {
                AdvancedOptionsErrors.SetError(ProfileNameText, err.Message);
                e.Cancel = true;
            }
        }


        /// <summary>
        /// Event handler for validation request, calls validation method for service URL
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ServiceURLText_Validating(object sender, CancelEventArgs e)
        {
            try
            {
                _ValidURL = CheckWebServiceURL(ServiceURLText.Text);
                if (_ValidURL)
                {
                    AdvancedOptionsErrors.SetError(ServiceURLText, "");
                }
                else
                {
                    AdvancedOptionsErrors.SetError(ServiceURLText, "A valid URL is required");
                    e.Cancel = true;
                }
            }
            catch (Exception err)
            {
                AdvancedOptionsErrors.SetError(ServiceURLText, err.Message);
                e.Cancel = true;
            }
        }

        /// <summary>
        /// Event handler for validation request, calls validation method for user key
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UserKeyText_Validating(object sender, CancelEventArgs e)
        {
            try
            {
                _ValidAccount = CheckUserKey(UserKeyText.Text);
                if (_ValidAccount)
                {
                    AdvancedOptionsErrors.SetError(UserKeyText, "");
                }
                else
                {
                    AdvancedOptionsErrors.SetError(UserKeyText, "A valid public key is required");
                    e.Cancel = true;
                }
            }
            catch (Exception err)
            {
                AdvancedOptionsErrors.SetError(UserKeyText, err.Message);
                e.Cancel = true;
            }
        }

        /// <summary>
        /// Event handler for validation request, calls validation method for encoded key
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void EncodedKeyText_Validating(object sender, CancelEventArgs e)
        {
            try
            {
                _ValidKey = CheckEncodedKey(EncodedKeyText.Text);
                if (_ValidKey)
                {
                    AdvancedOptionsErrors.SetError(EncodedKeyText, "");
                }
                else
                {
                    AdvancedOptionsErrors.SetError(EncodedKeyText, "A valid private key is required. Please check and re-enter your private key.");
                    e.Cancel = true;
                }
            }
            catch (Exception err)
            {
                AdvancedOptionsErrors.SetError(EncodedKeyText, err.Message);
                e.Cancel = true;
            }
        }
        #endregion // Textbox event handlers

        #region Private user entry validation functions
        /// <summary>
        /// Method to perform any and all validation of profile name
        /// </summary>
        /// <param name="profileName">Profile name input from user</param>
        /// <returns>Boolean indication of valid profile name in terms of length and content</returns>
        private bool CheckProfileName(string profileName)
        {
            bool bRet = false;
            if (!String.IsNullOrEmpty(profileName))
            {
                // Check that it is a valid key structure
                Regex regKey = new Regex(@"(\w(\s)?)+", RegexOptions.None);
                if (regKey.IsMatch(profileName))
                {
                    bRet = true;
                }
            }

            return bRet;
        }

        /// <summary>
        /// Method to perform any and all validation of web service URL
        /// </summary>
        /// <param name="serviceUrl">URL input from user</param>
        /// <returns>Boolean indication of valid web service URL</returns>
        private bool CheckWebServiceURL(string serviceUrl)
        {
            bool bRet = false;
            // Check that it's not empty
            if (!String.IsNullOrEmpty(serviceUrl))
            {
                // Check that it is a valid web URL
                Regex regURL = new Regex(@"^(http|https)\://[a-zA-Z0-9\-\.]+\.[a-zA-Z0-9]{2,4}(:[a-zA-Z0-9]*)?/?([a-zA-Z0-9\-_/\\\~])*$", RegexOptions.None);
                if (regURL.IsMatch(serviceUrl))
                {
                    LXAWebService wsLXA = new LXAWebService();
                    try
                    {
                        bRet = wsLXA.WSServiceCheck(serviceUrl);
                    }
                    catch (Exception err)
                    {
                        MessageBox.Show("There was a problem checking the status of the Lexascope web service at the URL provided.\n" + err.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
                    }
                }
            }
            return bRet;
        }

        /// <summary>
        /// Method to perform any and all validation of user key
        /// </summary>
        /// <param name="userKey">Account key input from user</param>
        /// <returns>Boolean indication of valid web service URL</returns>
        private bool CheckUserKey(string userKey)
        {
            bool bRet = false;
            if (!String.IsNullOrEmpty(userKey))
            {
                // Check that it is a valid key structure
                Regex regKey = new Regex(@"^([a-zA-Z0-9]){16}$", RegexOptions.None);
                if (regKey.IsMatch(userKey))
                {
                    bRet = true;
                }
            }

            return bRet;
        }

        /// <summary>
        /// Method to perform any and all validation of encoded key
        /// </summary>
        /// <param name="encodedKey">Account key input from user</param>
        /// <returns>Boolean indication of valid encoded key in terms of length</returns>
        private bool CheckEncodedKey(string encodedKey)
        {
            bool bRet = false;
            if (!String.IsNullOrEmpty(encodedKey))
            {
                // Check that it is a valid encoded key structure
                if (encodedKey.Length == 36)
                {
                    bRet = true;
                }
            }

            return bRet;
        }

        #endregion // Private user entry validation functions

        #region Handlers for seeking local profile components
        private void btnLicense_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlgLicense = new OpenFileDialog();
            dlgLicense.CheckFileExists = true;
            dlgLicense.DefaultExt = ".v5";
            dlgLicense.Filter = "Lexalytics Salience Five license (*.v5)|*.v5";
            dlgLicense.Multiselect = false;
            dlgLicense.RestoreDirectory = true;
            dlgLicense.Title = "Select Lexalytics license file";
            if (dlgLicense.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                txtLicensePath.Text = dlgLicense.FileName;
                System.IO.FileInfo oFile = new System.IO.FileInfo(txtLicensePath.Text);
                if (txtDataDirectory.Text.Length == 0)
                {
                    txtDataDirectory.Text = oFile.DirectoryName + "\\data";
                    if (txtUserDirectory.Text.Length == 0)
                        txtUserDirectory.Text = txtDataDirectory.Text + "\\user";
                }
            }
        }

        private void btnDataDir_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog dlgDataDir = new FolderBrowserDialog();
            if (this.txtDataDirectory.Text.Length > 0 && System.IO.Directory.Exists(txtDataDirectory.Text))
                dlgDataDir.SelectedPath = txtDataDirectory.Text;
            dlgDataDir.ShowNewFolderButton = false;
            dlgDataDir.Description = "Select the Salience data directory to use.";
            if (dlgDataDir.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                txtDataDirectory.Text = dlgDataDir.SelectedPath;
                if (txtUserDirectory.Text.Length == 0)
                    txtUserDirectory.Text = txtDataDirectory.Text + "\\user";
            }
        }

        private void btnUserDir_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog dlgUserDir = new FolderBrowserDialog();
            if (this.txtUserDirectory.Text.Length > 0 && System.IO.Directory.Exists(txtUserDirectory.Text))
                dlgUserDir.SelectedPath = txtUserDirectory.Text;
            dlgUserDir.ShowNewFolderButton = false;
            dlgUserDir.Description = "Select the Salience user directory to use.";
            if (dlgUserDir.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                txtUserDirectory.Text = dlgUserDir.SelectedPath;
            }

        }

        #endregion

    }
}
